﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using Microsoft.SharePoint;
using DE.Sdc.SharePoint.SPContentStructure.Common;

namespace DE.Sdc.SharePoint.SPContentStructure.Import
{
    [Serializable]
    public class Field : IXmlSerializable, IImportDefinition<SPField, SPList>
    {
        public XmlElement Xml { get; set; }

        [XmlIgnore]
        public ActionType Action
        {
            get
            {
                if (Xml != null && Xml.Attributes["Action"] != null && Xml.Attributes["Action"].Value != null)
                    return (ActionType)Enum.Parse(typeof(ActionType), Xml.Attributes["Action"].Value, true);

                return ActionType.AddOrUpdate;
            }
            set
            {
                if (Xml.Attributes["Action"] == null)
                {
                    Xml.SetAttribute("Action", value.ToString());
                }
                else
                {
                    Xml.Attributes["Action"].Value = value.ToString();
                }

            }
        }

        [XmlIgnore]
        public bool AddToExistingContentTypes
        {
            get
            {
                if (Xml != null && Xml.Attributes["AddToExistingContentTypes"] != null && Xml.Attributes["AddToExistingContentTypes"].Value != null)
                    return bool.Parse(Xml.Attributes["AddToExistingContentTypes"].Value);

                return false;
            }
            set
            {
                if (Xml.Attributes["AddToExistingContentTypes"] == null)
                {
                    Xml.SetAttribute("AddToExistingContentTypes", value.ToString());
                }
                else
                {
                    Xml.Attributes["AddToExistingContentTypes"].Value = value.ToString();
                }

            }
        }

        [XmlIgnore]
        public string Name
        {
            get
            {
                if (Xml != null)
                    return Xml.Attributes["Name"].Value;

                return null;
            }
        }

        [XmlIgnore]
        public string DisplayName
        {
            get
            {
                if (Xml != null)
                    return Xml.Attributes["DisplayName"].Value;

                return null;
            }
            set
            {
                if (Xml != null)
                    Xml.Attributes["DisplayName"].Value = value;
            }
        }

        [XmlIgnore]
        public Guid Id
        {
            get
            {
                if (Xml != null)
                    return new Guid(Xml.Attributes["ID"].Value);

                return Guid.Empty;
            }
            set
            {
                if (Xml != null)
                    Xml.Attributes["ID"].Value = value.ToString();
            }
        }

        [XmlIgnore]
        public string LookupList
        {
            get
            {
                if (Xml != null)
                    return Xml.Attributes["List"].Value;

                return string.Empty;
            }
            set
            {
                if (Xml != null)
                    Xml.Attributes["List"].Value = value.ToString();
            }
        }

        [XmlIgnore]
        public string LookupWebId
        {
            get
            {
                if (Xml != null)
                    return Xml.Attributes["WebId"].Value;

                return string.Empty;
            }
            set
            {
                if (Xml != null)
                    Xml.Attributes["WebId"].Value = value.ToString();
            }
        }

        [XmlIgnore]
        public SPField UpdatedSPObject { get; internal set; }

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(XmlReader reader)
        {
            XmlDocument xml = new XmlDocument();
            xml.LoadXml(reader.ReadOuterXml());
            Xml = xml.DocumentElement;
        }

        public void WriteXml(XmlWriter writer)
        {
            if (Xml != null)
            {
                for (int i = 0; i < Xml.Attributes.Count; i++)
                {
                    XmlAttribute attr = Xml.Attributes[i];
                    writer.WriteAttributeString(attr.Name, attr.Value);
                }

                for (int i = 0; i < Xml.ChildNodes.Count; i++)
                {

                    XmlNode node = Xml.ChildNodes[i];
                    writer.WriteNode(node.CreateNavigator(), true);
                }


                //writer.WriteNode(Xml.CreateNavigator(), true);
            }
        }


        public void ImportDefinition(SPField field, SPList list)
        {
            switch (Action)
            {
                case ActionType.Replace:
                    if (field != null)
                        field.Delete();
                    break;
                case ActionType.AddOrUpdate:
                    {
                        if (field == null && Xml != null)
                        {
                            XmlNode webIdNode = Xml.Attributes.GetNamedItem("WebId");
                            if (webIdNode != null)
                            {
                                if (string.IsNullOrEmpty(webIdNode.Value))
                                {
                                    webIdNode.Value = list.ParentWeb.ID.ToString();
                                }
                                else
                                {
                                    var resolvedWebIdNodeValue = DeploymentHelper.Resolve(webIdNode.Value, list.ParentWeb.Locale);
                                    try
                                    {
                                        webIdNode.Value = new Guid(resolvedWebIdNodeValue).ToString();
                                    }
                                    catch (FormatException)
                                    {
                                        SPWebCollection webs = list.ParentWeb.Site.AllWebs;

                                        using (SPWeb web = webs[resolvedWebIdNodeValue]) 
                                        {
                                            webIdNode.Value = web.ID.ToString();
                                        }
                                    }
                                }
                            }
                            XmlNode listNode = Xml.Attributes.GetNamedItem("List");
                            if (listNode != null)
                            {
                                if (string.IsNullOrEmpty(listNode.Value))
                                {
                                    listNode.Value = list.ID.ToString();
                                }
                                else
                                {
                                    var resolvedListNodeValue = DeploymentHelper.Resolve(listNode.Value, list.ParentWeb.Locale);
                                    try
                                    {
                                        listNode.Value = new Guid(resolvedListNodeValue).ToString();
                                    }
                                    catch (FormatException)
                                    {
                                        if ((webIdNode != null) && !string.IsNullOrEmpty(webIdNode.Value))
                                        {
                                            using (
                                                var lookupWeb = list.ParentWeb.Site.OpenWeb(new Guid(webIdNode.Value)))
                                            {
                                                listNode.Value = lookupWeb.Lists[resolvedListNodeValue].ID.ToString();
                                            }
                                        }
                                        else
                                        {
                                            listNode.Value = list.ParentWeb.Lists[resolvedListNodeValue].ID.ToString();
                                        }
                                    }
                                }
                            }

                            XmlNode showFieldNode = Xml.Attributes.GetNamedItem("ShowField");
                            if (showFieldNode != null)
                            {
                                var resolvedShowFieldNodeValue = DeploymentHelper.Resolve(showFieldNode.Value,
                                                                                          list.ParentWeb.Locale);
                                try
                                {
                                    showFieldNode.Value = new Guid(resolvedShowFieldNodeValue).ToString();
                                }
                                catch (FormatException)
                                {
                                    if ((webIdNode != null) && !string.IsNullOrEmpty(webIdNode.Value) &&
                                        (listNode != null) && !string.IsNullOrEmpty(listNode.Value))
                                    {
                                        using (
                                            var lookupWeb = list.ParentWeb.Site.OpenWeb(new Guid(webIdNode.Value)))
                                        {
                                            showFieldNode.Value =
                                                lookupWeb.Lists[new Guid(listNode.Value)].Fields.
                                                    GetFieldByInternalName(resolvedShowFieldNodeValue).Id.ToString();
                                        }
                                    }
                                    else if ((listNode != null) && !string.IsNullOrEmpty(listNode.Value))
                                    {
                                        showFieldNode.Value =
                                            list.ParentWeb.Lists[new Guid(listNode.Value)].Fields.
                                                GetFieldByInternalName(resolvedShowFieldNodeValue).Id.ToString();
                                    }
                                }
                            }

                            string fieldDisplayName = DeploymentHelper.Resolve(DisplayName, list.ParentWeb.Locale);
                            string fieldName = Name;
                            string fieldXml = DeploymentHelper.RemoveNamespace(Xml.OuterXml, Xml.NamespaceURI);

                            //DisplayName durch Namen ersetzen, da AddFieldAsXml DisplayName als InternalName nutzt
                            XDocument xmlDocument = XDocument.Parse(fieldXml);
                            XElement xmlElement = xmlDocument.XPathSelectElement("/Field");
                            xmlElement.Attribute("DisplayName").Value = fieldName;
                            fieldXml = xmlDocument.ToString();

                            fieldName = list.Fields.AddFieldAsXml(fieldXml, true,
                                                                  list.ContentTypesEnabled
                                                                      ? SPAddFieldOptions.AddToNoContentType
                                                                      : SPAddFieldOptions.Default);
                            field = list.Fields.GetField(fieldName);
                            field.Title = fieldDisplayName;
                            field.Update();

                            if (AddToExistingContentTypes)
                               AddFieldToExisitingContentTypes(field, list);

                        }
                        else if (Action == ActionType.AddOrUpdate && Xml != null && !Xml.Attributes["Type"].Value.Contains("Lookup"))
                        //HACK: Lookupfield liefert bei Vorhandensein System.ArgumentException
                        {
                            try
                            {
                                field = list.Fields.GetField(Name);
                                field.SchemaXml = Xml.OuterXml;
                                field.Update();
                            }
                            catch (ArgumentException)
                            {
                                Trace.WriteLine("DE.Sdc.SharePoint.SPContentStructure.Import.Fields.ImportDefinition: ArgumentException ignored!");
                            }
                        }
                    }
                    break;
                case ActionType.Delete:
                    if (field != null)
                        field.Delete();
                    break;

            }

            UpdatedSPObject = field;
        }

        private static void AddFieldToExisitingContentTypes(SPField field, SPList list)
        {
            foreach (SPContentType contentType in list.ContentTypes)
            {
                if (!contentType.Sealed && !contentType.Fields.ContainsField(field.StaticName))
                    contentType.FieldLinks.Add(new SPFieldLink(field));
            }
        }
    }
}
